package xyz.jcat.web.security;

import lombok.Getter;
import lombok.Setter;
import org.springframework.http.HttpMethod;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import xyz.jcat.common.Const;
import xyz.jcat.common.util.ArrayUtils;
import xyz.jcat.common.util.ReflectUtils;
import xyz.jcat.web.security.annotation.Menu;
import xyz.jcat.web.security.annotation.RequiresPermission;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class PermissionUtils {

    public static List<MenuPermission> getMenuPermissions(String controllerPackage) {
        List<MenuPermission> list = new ArrayList<>();
        List<Class<?>> classes = ReflectUtils.getPackageClasses(controllerPackage);
        for (Class clazz : classes) {
            MenuPermission menuPermission = getMenuPermission(clazz);
            if(Objects.isNull(menuPermission))
                continue;

            list.add(menuPermission);
        }
        return list;
    }

    private static MenuPermission getMenuPermission(Class clazz) {
        MenuPermission menuPermission = null;
        Annotation menuAnn = clazz.getAnnotation(Menu.class);
        if(!Objects.isNull(menuAnn)) {
            menuPermission = getMenuPermission((Menu) menuAnn);
        }else {
            return menuPermission;
        }
        String uriPrefix = "";
        Annotation requestMappingAnn = clazz.getAnnotation(RequestMapping.class);
        if(!Objects.isNull(requestMappingAnn)) {
            uriPrefix = getPath(((RequestMapping) requestMappingAnn).value());
        }
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            RequiresPermission requiresPermission = method.getAnnotation(RequiresPermission.class);
            if(Objects.isNull(requiresPermission))
                continue;

            ButtonPermission buttonPermission = new ButtonPermission();
            buttonPermission.setName(requiresPermission.name());
            buttonPermission.setPermission(requiresPermission.value());
            //方法和uri
            GetMapping getMapping = method.getAnnotation(GetMapping.class);
            if(!Objects.isNull(getMapping)) {
                buttonPermission.setReqMethod(HttpMethod.GET.name());
                buttonPermission.setReqUri(getUri(uriPrefix, getMapping.value()));
            }

            PostMapping postMapping = method.getAnnotation(PostMapping.class);
            if(!Objects.isNull(postMapping)) {
                buttonPermission.setReqMethod(HttpMethod.POST.name());
                buttonPermission.setReqUri(getUri(uriPrefix, postMapping.value()));
            }

            DeleteMapping deleteMapping = method.getAnnotation(DeleteMapping.class);
            if(!Objects.isNull(deleteMapping)) {
                buttonPermission.setReqMethod(HttpMethod.DELETE.name());
                buttonPermission.setReqUri(getUri(uriPrefix, deleteMapping.value()));
            }

            PutMapping putMapping = method.getAnnotation(PutMapping.class);
            if(!Objects.isNull(putMapping)) {
                buttonPermission.setReqMethod(HttpMethod.PUT.name());
                buttonPermission.setReqUri(getUri(uriPrefix, putMapping.value()));
            }

            PatchMapping patchMapping = method.getAnnotation(PatchMapping.class);
            if(!Objects.isNull(patchMapping)) {
                buttonPermission.setReqMethod(HttpMethod.PATCH.name());
                buttonPermission.setReqUri(getUri(uriPrefix, patchMapping.value()));
            }
            menuPermission.getButtons().add(buttonPermission);
        }
        return menuPermission;
    }

    private static MenuPermission getMenuPermission(Menu menu) {
        return new MenuPermission(menu.name(), menu.value());
    }

    private static String getPath(String[] paths) {
        if(ArrayUtils.isNotEmpty(paths)) {
            String path = paths[0];
            if(!path.startsWith("/"))
                return "/"+path;
            return path;
        }
        return Const.EMPTY;
    }

    private static String getUri(String uriPrefix, String[] paths) {
        return uriPrefix + getPath(paths);
    }

    @Getter
    @Setter
    public static class MenuPermission {
        private String name;
        private String permission;
        private List<ButtonPermission> buttons;

        public MenuPermission(String name, String permission) {
            this.name = name;
            this.permission = permission;
            this.buttons = new ArrayList<>();
        }
    }
    @Getter
    @Setter
    public static class ButtonPermission {
        /** 权限名称 */
        private String name;
        /** 权限标识 */
        private String permission;
        /** 请求uri */
        private String reqUri;
        /** 请求方式 */
        private String reqMethod;
    }
}
